package net

import (
	"common/logs"
	"connector/protocol"
	"errors"
	"fmt"
	"github.com/google/uuid"
	"github.com/gorilla/websocket"
	"net/http"
	"sync"
	"time"
)

type CheckOrigin func(r *http.Request) bool

const (
	PingWait       = 10 * time.Second
	PongWait       = 10 * time.Second    // pong 等待时间
	PingInterval   = (PongWait * 9) / 10 // ping 时间间隔
	MaxMessageSize = 1024
)

var (
	eventHandler *EventHandler
)

type WsManager struct {
	sync.RWMutex
	websocketUpgrade *websocket.Upgrader
	ServerId         string
	CheckOrigin      CheckOrigin
	clients          map[string]*WsConnection // WebSocket 客户端数组
}

// WsConnection 管理每一个 WebSocket 连接
type WsConnection struct {
	Cid               string
	Conn              *websocket.Conn
	Session           *Session
	ReadChan          chan *MsgPack
	WriteChan         chan []byte
	writeHandlerChan  chan bool
	clientHandlerChan chan bool
	pingTicker        *time.Ticker
}

type MsgPack struct {
	Cid  string
	Body []byte
}

func NewWsConnection(cid string, conn *websocket.Conn) *WsConnection {
	return &WsConnection{
		Cid:               cid,
		Conn:              conn, // WebSocket 连接
		Session:           NewSession(cid),
		WriteChan:         make(chan []byte, MaxMessageSize),
		ReadChan:          make(chan *MsgPack, MaxMessageSize),
		writeHandlerChan:  make(chan bool),
		clientHandlerChan: make(chan bool),
	}
}

// Run 启动服务
func (m *WsManager) Run(addr string) {
	// 1. 初始化 EventHandler
	eventHandler = NewEventHandler(m.ServerId, m.clients)
	// 2. 注册 LogicHandler
	RegisterLogicHandler(eventHandler)
	// 4. 启动 Web 服务
	http.HandleFunc("/", m.serverWebSocket)
	logs.Info("serverId [ %s ] is running.", m.ServerId)
	if err := http.ListenAndServe(addr, nil); err != nil {
		logs.Fatal("connector listen serve err:%v", err)
	}
}

// 将服务升级为 WebSocket 服务
func (m *WsManager) serverWebSocket(writer http.ResponseWriter, request *http.Request) {
	if m.websocketUpgrade == nil {
		m.websocketUpgrade = &websocket.Upgrader{
			CheckOrigin: func(r *http.Request) bool {
				return true
			},
			ReadBufferSize:  MaxMessageSize,
			WriteBufferSize: MaxMessageSize,
		}
	}
	// 1. 创建 WebSocket 服务
	wsConn, err := m.websocketUpgrade.Upgrade(writer, request, nil)
	if err != nil {
		logs.Fatal("websocket upgrade err:%v", err)
	}
	// 2. 为客户端生成一个唯一 ID
	cid := fmt.Sprintf("%s-%s", m.ServerId, uuid.New().String())
	// 当前连接放入到 manager
	websocketConn := NewWsConnection(cid, wsConn)
	m.addClient(websocketConn)
	// 3. 运行 WebSocket 服务
	websocketConn.Run(m)
}

// clientReadChanHandler 从 ReadChannel 中读取数据
func (m *WsConnection) clientReadChanHandler() {
	for {
		select {
		case body, ok := <-m.ReadChan:
			if ok {
				// 进行数据解析
				m.decodePack(body)
			}
		case <-m.clientHandlerChan:
			return
		}
	}
}

// decodePack 解析数据
func (m *WsConnection) decodePack(body *MsgPack) {
	logs.Debug("receiver message:%v", string(body.Body))
	// 解析数据
	packet, err := protocol.Decode(body.Body)
	if err != nil {
		logs.Error("cid:%s 消息解析异常：%v", body.Cid, err)
		return
	}
	if err := m.routeEvent(packet); err != nil {
		logs.Error("%v", err)
		return
	}
}

// readMessage WebSocket 读消息
func (m *WsConnection) readMessage(manager *WsManager) {
	defer func() {
		// 关闭连接并移除客户端
		manager.removeClient(m.Cid)
		// 通知其他 go routing 关闭服务关闭
		m.writeHandlerChan <- true
	}()
	// 最大读取
	m.Conn.SetReadLimit(MaxMessageSize)
	for {
		// 阻塞读取消息
		messageType, message, err := m.Conn.ReadMessage()
		if err != nil {
			break
		}
		// 目前客户端发送的都是 BinaryMessage
		if messageType == websocket.BinaryMessage {
			// 向通道中写入数据
			m.ReadChan <- &MsgPack{
				Cid:  m.Cid,
				Body: message,
			}
		} else {
			logs.Error("unsupported message type : %v", messageType)
		}
	}
}

// writeMessage WebSocket 写消息
func (m *WsConnection) writeMessage() {
	m.pingTicker = time.NewTicker(PingInterval)
	for {
		select {
		case message, ok := <-m.WriteChan:
			if !ok {
				if err := m.Conn.WriteMessage(websocket.CloseMessage, nil); err != nil {
					logs.Error("connection closed, err:%v,cid:%d", err, m.Cid)
				}
				return
			}
			// 向客户端写消息
			if err := m.Conn.WriteMessage(websocket.BinaryMessage, message); err != nil {
				logs.Error("client write message err:%v,cid:%d", err, m.Cid)
			}
		case <-m.pingTicker.C:
			if err := m.Conn.SetWriteDeadline(time.Now().Add(PingWait)); err != nil {
				logs.Error("client ping err:%v,cid:%d", err, m.Cid)
			}
			logs.Debug("cid %s ping success.", m.Cid)
			// ping 操作
			if err := m.Conn.WriteMessage(websocket.PingMessage, nil); err != nil {
				logs.Error("client ping message err:%v,cid:%d", err, m.Cid)
				m.Close()
			}
		case <-m.writeHandlerChan:
			m.pingTicker.Stop()
			// 通知其他 go routing 关闭服务关闭
			m.clientHandlerChan <- true
			return
		}
	}
}

// Close 关闭与客户端建立的连接
func (m *WsConnection) Close() {
	if m.Conn != nil {
		if err := m.Conn.Close(); err != nil {
			logs.Error("close connection err:%v,cid:%s", err, m.Cid)
		}
	}
	if m.pingTicker != nil {
		m.pingTicker.Stop()
	}
}

// Run 启动 WebSocket
func (m *WsConnection) Run(manager *WsManager) {
	// 设置 ping pong 超时时间
	m.Conn.SetPongHandler(m.PongHandler)
	go m.readMessage(manager)
	go m.writeMessage()
	go m.clientReadChanHandler()
}

// PongHandler pong 超时时间
func (m *WsConnection) PongHandler(data string) error {
	logs.Debug("cid %s pong success. %s", m.Cid, data)
	if err := m.Conn.SetReadDeadline(time.Now().Add(PongWait)); err != nil {
		return err
	}
	return nil
}

// routeEvent 根据事件类型进行路由
func (m *WsConnection) routeEvent(packet *protocol.Packet) error {
	// 获取消息处理器
	h, ok := eventHandler.Handlers[packet.Type]
	if ok {
		return h(packet, m)
	}
	return errors.New("没有找到 packet " + string(packet.Type))
}

// removeClient 移除客户端
func (m *WsManager) removeClient(cid string) {
	conn, ok := m.clients[cid]
	if ok {
		// 关闭连接
		conn.Close()
		// 删除元素
		delete(m.clients, cid)
	}
}

// 添加客户端
func (m *WsManager) addClient(client *WsConnection) {
	m.Lock()
	defer m.Unlock()
	m.clients[client.Cid] = client
}

// Close 关闭服务
func (m *WsManager) Close() {
	// 遍历 WebSocket 客户端并移除所有客户端
	for _, client := range m.clients {
		m.removeClient(client.Cid)
	}
}

// SendMessage 发送消息
func (c *WsConnection) SendMessage(buf []byte) error {
	c.WriteChan <- buf
	return nil
}

func NewWsManager() *WsManager {
	return &WsManager{
		clients: make(map[string]*WsConnection),
	}
}
